Skip to content

feat(read-state): multi-slot splitting + no-op suppression for oversized blobs#1309

Merged
wpfleger96 merged 2 commits into
mainfrom
duncan/readstate-semantic-eviction-multiSlot
Jun 29, 2026
Merged

feat(read-state): multi-slot splitting + no-op suppression for oversized blobs#1309
wpfleger96 merged 2 commits into
mainfrom
duncan/readstate-semantic-eviction-multiSlot

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Problem

The ReadStateManager publishes a single encrypted JSON blob (kind:30078) containing all read-state entries. As users accumulate channels and thread/message entries, the blob exceeds NIP-44's 65,535-byte plaintext cap — causing "publish failed: events too long" and breaking cross-device read-state sync.

Solution

Three complementary mechanisms ensure the blob always fits:

Multi-slot splitting

When channel keys alone exceed the 32 KB self-imposed budget (even after evicting all thread/msg entries), partition channel keys round-robin across up to 8 separate kind:30078 events, each with its own read-state:<slotId> d-tag. Thread/msg entries go into the primary slot only and are trimmed to budget there. Extra slot IDs persist to localStorage so they survive restarts. On fetch, all own-slot blobs are max-merged per key.

No-op suppression

Before publishing, compare the about-to-publish contexts against lastPublishedContexts. If identical, skip the publish entirely — avoids wasting bandwidth and relay writes on every 5-second debounce cycle when nothing changed.

NIP-09 cleanup

When transitioning from split mode (multiple slots) back to single mode, publish kind:5 delete events for stale extra-slot blobs. Without this, fetchOwnBlobBeforePublish keeps finding old blobs, merging stale keys, and the no-op check never fires — causing an infinite re-publish loop.

Files changed

  • desktop/src/features/channels/readState/readStateManager.tssplitContextsIntoBudgetedSlots, publishSplitSlots, publishOneSlot, deleteExtraSlots, no-op suppression in publish()
  • desktop/src/features/channels/readState/readStateManager.test.mjs — 5 split tests + no-op suppression integration test
  • desktop/src/features/channels/readState/readStateFormat.tsMSG_PREFIX, THREAD_PREFIX constants (shared with byte-budget trim)
  • desktop/scripts/check-file-sizes.mjs — updated override for readStateManager.ts (1019 lines, queued to split)

Testing

1242/1242 desktop tests pass. Biome clean. File-size check passes.

@wpfleger96 wpfleger96 marked this pull request as draft June 26, 2026 17:39
@wpfleger96 wpfleger96 marked this pull request as ready for review June 29, 2026 16:53
@wpfleger96 wpfleger96 force-pushed the duncan/readstate-semantic-eviction-multiSlot branch from 6b33140 to e4b48e7 Compare June 29, 2026 16:58
…zed blobs

When the read-state blob exceeds NIP-44's plaintext cap, split channel
keys round-robin across up to 8 kind:30078 slots. Thread/msg entries
stay in the primary slot and are trimmed to budget. No-op suppression
skips redundant publishes when the blob hasn't changed. NIP-09 delete
events clean up stale extra-slot blobs when transitioning back to
single-slot mode.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the duncan/readstate-semantic-eviction-multiSlot branch from e4b48e7 to 081f3f4 Compare June 29, 2026 17:41
@wpfleger96 wpfleger96 changed the title feat(read-state): semantic eviction + multi-slot splitting feat(read-state): multi-slot splitting + no-op suppression for oversized blobs Jun 29, 2026
…-ID persistence guard

The splitContextsIntoSlots JSDoc still referenced 'already semantically
evicted' after semantic eviction was removed. Updated to match the
current behavior. Added a comment explaining why length-only comparison
is sufficient for the extra-slot-ID persistence check.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 merged commit 2612324 into main Jun 29, 2026
25 checks passed
@wpfleger96 wpfleger96 deleted the duncan/readstate-semantic-eviction-multiSlot branch June 29, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant